package com.system.manager.common.interceptor;

import com.system.manager.common.annotation.PreAuthorize;
import com.system.manager.common.constant.HttpRequestHeaderNames;
import com.system.manager.common.constant.SysConstants;
import com.system.manager.modules.authorized.entity.Subject;
import com.system.manager.common.entity.TokenInfo;
import com.system.manager.modules.authorized.enums.AuthorizationErrorCode;
import com.system.manager.modules.authorized.enums.SubjectType;
import com.system.manager.modules.authorized.exception.AuthorizationException;
import com.system.manager.utils.RequestUtils;
import com.system.manager.utils.ThreadLocalMap;
import com.system.manager.utils.TokenManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Classname TokenInterceptor
 * @Description TODO 用来验证token的拦截器
 * @Date 2021/10/17 18:48
 * @Created by 杨小鹏：【241960695@qq.com】
 */
@Slf4j
public class TokenInterceptor implements HandlerInterceptor {
    private static final String BEARER_AUTHORIZATION_START = "Bearer";

    private TokenManager tokenManager;

    public TokenInterceptor(TokenManager tokenManager) {
        this.tokenManager = tokenManager;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 判断当前的handler是否是HandlerMethod
        if(!(handler instanceof HandlerMethod)){
            throw new AuthorizationException(AuthorizationErrorCode.REQUEST_PATH_ERROR);
        }

        String authorization = request.getHeader(HttpRequestHeaderNames.AUTHORIZATION);

        // 构建一个非法用户，交给下一个拦截器进行验证
        Subject illegalUser = Subject.getIllegalUser(RequestUtils.getIpAddr(request));

        // 1.未找到认证信息
        if (StringUtils.isEmpty(authorization)) {
            ThreadLocalMap.put(SysConstants.HTTP_ATTRIBUTE_SUBJECT, illegalUser);
            request.setAttribute(SysConstants.AUTHORIZATION_ERROR_CODE, AuthorizationErrorCode.NON_HEADER_AUTHORIZATION);
            return true;
        }

        // 2.找到认证信息但是认证信息不符合格式
        if (!authorization.startsWith(BEARER_AUTHORIZATION_START) || authorization.length() <= BEARER_AUTHORIZATION_START.length() + 1) {
            ThreadLocalMap.put(SysConstants.HTTP_ATTRIBUTE_SUBJECT, illegalUser);
            request.setAttribute(SysConstants.AUTHORIZATION_ERROR_CODE, AuthorizationErrorCode.ILLEGAL_HEADER_AUTHORIZATION);
            return true;
        }

        // 3.找到认证信息，进行认证信息正确性校验,并生成Subject
        // 3.1 获取PreAuthorize
        PreAuthorize annotation = ((HandlerMethod) handler).getMethod().getAnnotation(PreAuthorize.class);

        // 3.3 判断注解值是否为空
        if (annotation == null) {
            // 为空，交给AccessInterceptor拦截器进一步判断
            ThreadLocalMap.put(SysConstants.HTTP_ATTRIBUTE_SUBJECT, illegalUser);

            request.setAttribute(SysConstants.AUTHORIZATION_ERROR_CODE, AuthorizationErrorCode.IDENTITY_INFORMATION);
            return true;
        }


        // 3.2 获取当前接口的权限
        SubjectType[] values = annotation.value();


        // 4. 获取token
        String token = authorization.substring(BEARER_AUTHORIZATION_START.length() + 1);

        // 4.1 根据token判断redis中是否存在或者过期
        TokenInfo tokenInfo = tokenManager.getTokenInfo(token);

        if (null == tokenInfo) {
            // 如果过期，返回提示信息
            throw new AuthorizationException(AuthorizationErrorCode.SIGN_VERIFY_FAILURE);
        }

        // 4.2 判断当前用户的角色是否满足接口的权限
        boolean isRelease = false;
        for (SubjectType type : values) {
            // 如果当前的用户是匿名用户，表示谁都可以携带token访问，直接放行
            if (type.equals(SubjectType.ANONYMOUS)) {
                isRelease = true;
                break;
            }
            if (type.equals(SubjectType.valueOf(tokenInfo.getRole()))) {
                // 如果角色可以访问，则放行
                isRelease = true;
                break;
            }
        }
        // 4.3 当前角色不能访问，不放行
        if (!isRelease) {
            throw new AuthorizationException(AuthorizationErrorCode.IDENTITY_INFORMATION);
        }

        // 4.4 构建一个经过系统认证的subject，并存放到ThreadLocal中
        Subject authenticatedSubject = Subject.authenticated(tokenInfo.getUserId(), RequestUtils.getIpAddr(request), tokenInfo.getRole());
        ThreadLocalMap.put(SysConstants.HTTP_ATTRIBUTE_SUBJECT, authenticatedSubject);

        // 5. 放行
        return true;
    }

}
